#include <vector>
#include <iostream>
#include <string>
#include <any>
#include <string>

template <typename T>
void firstTest(T t);

class Variant
{
public:
    Variant() noexcept : d() {}
    ~Variant();
    Variant(const Variant &other);
    Variant(std::any &&other) noexcept;
    Variant(const std::any &other);

    Variant(int i);
    Variant(unsigned int ui);
    Variant(long long ll);
    Variant(unsigned long long ull);
    Variant(bool b);
    Variant(double d);
    Variant(float f);

    Variant(const char *str);
    Variant(const std::string &str);

    std::string ValueType() const;

    inline Variant(Variant &&other) noexcept : d(other.d)
    {
        other.d = d;
    }

    Variant &operator=(const Variant &other)
    {
        this->d = other.d;
        return *this;
    }

    inline Variant &operator=(Variant &&other) noexcept
    {
        std::swap(d, other.d);
        return *this;
    }

    inline void swap(Variant &other) noexcept { std::swap(d, other.d); }

    int toInt(bool *ok = nullptr) const;
    unsigned int toUInt(bool *ok = nullptr) const;
    long long toLongLong(bool *ok = nullptr) const;
    unsigned long long toULongLong(bool *ok = nullptr) const;
    bool toBool() const;
    double toDouble(bool *ok = nullptr) const;
    float toFloat(bool *ok = nullptr) const;
    std::string toString() const;

    template <typename T>
    inline void setValue(const T &value)
    {
        d = value;
    }

    template <class Type, class... Args>
    inline void emplaceValue(Args &&...args)
    {
        d = std::move(std::make_any<Type>(std::forward<Args>(args)...));
    }

    template <typename T>
    inline T value() const
    {
        return std::any_cast<T>(d);
    }

    template <typename T>
    static inline Variant fromValue(const T &value)
    {
        return Variant(value);
    }

    template <class Type, class... Args>
    static inline Variant fromValue(Args &&...args)
    {
        return Variant(std::make_any<Type>(std::forward<Args>(args)...));
    }

private:
    std::any d;
};

class ObjA
{
public:
    ObjA(int value) : a(value) {}
    int a{111};
};

int main()
{
    // Variant var(1U);

    // auto value1 = var.toBool();
    // auto value2 = var.toInt();
    // auto value3 = var.toUInt();

    // std::cout << "value type:" << var.ValueType() << ",value:" << value1 << std::endl;
    // std::cout << "value type:" << var.ValueType() << ",value:" << value2 << std::endl;
    // std::cout << "value type:" << var.ValueType() << ",value:" << value3 << std::endl;

    // auto objValue = Variant::fromValue<ObjA>(222);
    // auto obj = objValue.value<ObjA>();
    // std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    // auto objValue = Variant::fromValue<ObjA>(ObjA(333));
    // auto obj = objValue.value<ObjA>();
    // std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    auto objValue = Variant();
    objValue.emplaceValue<ObjA>(444);
    auto obj = objValue.value<ObjA>();
    std::cout << "value type:" << objValue.ValueType() << ",value:" << obj.a << std::endl;

    return 0;
}

template <typename T>
void firstTest(T t)
{
    std::any a;
    a = t;

    if (a.type() == typeid(std::string))
    {
        auto var = std::any_cast<std::string>(a);
        std::cout << "Get string " << var << std::endl;
    }
    else if (a.type() == typeid(int))
    {
        auto var = std::any_cast<int>(a);
        std::cout << "Get int " << var << std::endl;
    }
    else if (a.type() == typeid(float))
    {
        auto var = std::any_cast<float>(a);
        std::cout << "Get float " << var << std::endl;
    }
    else if (a.type() == typeid(double))
    {
        auto var = std::any_cast<double>(a);
        std::cout << "Get double " << var << std::endl;
    }
    else
    {
        std::cout << "Unknown value type." << std::endl;
    }
}

Variant::~Variant()
{
    d.reset();
}

Variant::Variant(const Variant &other)
{
    d = other.d;
}

Variant::Variant(std::any &&other) noexcept
{
    d.swap(other);
}

Variant::Variant(const std::any &other)
{
    d = other;
}

Variant::Variant(int i)
{
    d = i;
}

Variant::Variant(unsigned int ui)
{
    d = ui;
}

Variant::Variant(long long ll)
{
    d = ll;
}

Variant::Variant(unsigned long long ull)
{
    d = ull;
}

Variant::Variant(bool b)
{
    d = b;
}

Variant::Variant(double d)
{
    this->d = d;
}

Variant::Variant(float f)
{
    d = f;
}

Variant::Variant(const char *str)
{
    d = std::string(str);
}

Variant::Variant(const std::string &str)
{
    d = str;
}

std::string Variant::ValueType() const
{
    if (d.has_value())
        return d.type().name();
    return "Unknown Type.";
}

int Variant::toInt(bool *ok) const
{
    if (d.type() == typeid(int))
    {
        if (ok)
            *ok = true;

        return std::any_cast<int>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

unsigned int Variant::toUInt(bool *ok) const
{
    if (d.type() == typeid(unsigned int))
    {
        if (ok)
            *ok = true;

        return std::any_cast<unsigned int>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

long long Variant::toLongLong(bool *ok) const
{
    if (d.type() == typeid(long long))
    {
        if (ok)
            *ok = true;

        return std::any_cast<long long>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

unsigned long long Variant::toULongLong(bool *ok) const
{
    if (d.type() == typeid(unsigned long long))
    {
        if (ok)
            *ok = true;

        return std::any_cast<unsigned long long>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

bool Variant::toBool() const
{
    if (d.type() == typeid(bool))
    {
        return std::any_cast<bool>(d);
    }
    return false;
}

double Variant::toDouble(bool *ok) const
{
    if (d.type() == typeid(double))
    {
        if (ok)
            *ok = true;

        return std::any_cast<double>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

float Variant::toFloat(bool *ok) const
{
    if (d.type() == typeid(float))
    {
        if (ok)
            *ok = true;

        return std::any_cast<float>(d);
    }
    if (ok)
        *ok = false;
    return 0;
}

std::string Variant::toString() const
{
    if (d.type() == typeid(std::string))
    {
        return std::any_cast<std::string>(d);
    }
    return std::string();
}